﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "GbmpActionBreak.h"

#include "ISelection.h"
#include "IPickFilter.h"
#include "IPickTarget.h"
#include "IDocument.h"
#include "IElement.h"
#include "IElementBasicInformation.h"
#include "IType.h"
#include "IElementParameters.h"
#include "IParameterDefinitionLibrary.h"
#include "IPositionCurve2d.h"
#include "IElementPosition.h"
#include "IGraphicsNodeReference.h"
#include "IPickPointAction.h"
#include "ISnapContext.h"
#include "ISnap.h"
#include "IArc3d.h"
#include "IUserTransaction.h"
#include "IElementBreakBehavior.h"


#include "IUiDocumentViewManager.h"
#include "IUiView.h"
#include "IModelView.h"
#include "IPlane.h"
#include "ILine3d.h"
#include "Coordinate3d.h"
#include "AlgorithmMinimalDistance.h"
#include "BreakUtils.h"
#include "NotificationUtils.h"
#include "IHighlights.h"
#include "GbmpBuiltInCategoryUniIdentitiesNew.h"
#include "GbmpBuiltInParameterDefinitionsNew.h"
#include "StructureInstanceSecondKeys.h"
#include "StructureWallBreakBehavior.h"
#include "StructureBeamBreakBehavior.h"
#include "IGenericElement.h"
#include "IDrawingAnnotationText.h"
#include "IDrawingPolyCurve.h"
#include "AlgorithmProject.h"
#include "GcmpBuiltInCategoryUniIdentities.h"
#include "IPolyCurve.h"
#include "IActionInputDialog.h"
#include "IActionInputEvent.h"
#include "IActionInputEventArgs.h"
#include "IRadioButtonGroupDefinition.h"
#include "Vector2dUtils.h"
#include "IArc2d.h"
#include "AlgorithmPositionJudge.h"
#include "IModelLine.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;

#define ACTION_BREAK_POLYCURVE  L"GbmpBreakPolyCurve"
const std::wstring s_titleOneBreak = GBMP_TR(L"单点打断");
const std::wstring s_titleTwoBreak = GBMP_TR(L"两点打断");
bool GbmpActionBreak::ms_twoPtBreak = false;
namespace gcmp
{
    class GbmpBreakPickFilterForLocalSnap : public IPickFilter
    {
    public:
        GbmpBreakPickFilterForLocalSnap(gcmp::IDocument* pDoc):m_pDoc(pDoc) {}

        bool AllowElement(const gcmp::ElementId& elemId) const override;

        bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const override;

        virtual bool SetPickTargetOption(IPickTarget* pickTarget) override;

    protected:
        const IDocument* GetDocument() const { return m_pDoc; }

    private:
        gcmp::IDocument* m_pDoc;
    };

    bool GbmpBreakPickFilterForLocalSnap::SetPickTargetOption(IPickTarget* pickTarget)
    {
        pickTarget->EnableGraphicsElementShape();
        return true;
    }

    bool GbmpBreakPickFilterForLocalSnap::AllowElement(const gcmp::ElementId& elemId) const
    {
        if (!m_pDoc)
            return false;

        const gcmp::IElement* element = GetDocument()->GetElement(elemId);
        DBG_WARN_AND_RETURN_FALSE_UNLESS(element != nullptr, L"element为空",L"GDMPLab",L"2024-03-30");

        if (const IGenericElement* pGenericElement = quick_cast<const IGenericElement>(element))
        {
            //【二维出图】文字不支持打断
            if (const IDrawingAnnotationText* pDrawingAnnotationText = dynamic_cast<const IDrawingAnnotationText*>(pGenericElement->GetExternalObject()))
            {
                return false;
            }

            //多段线打断
            if (const IDrawingPolyCurve* pDrawingAnnotationText = quick_cast<IDrawingPolyCurve>(pGenericElement->GetExternalObject()))
            {
                return true;
            }
        }

        const gcmp::IElementPosition *posBehavior = element->GetElementPosition();
        if (posBehavior != nullptr)
        {
            // 如果是变截面梁则不需要打断
            if ((element->GetBasicInformation()->GetCategoryUid() ==
                gcmp::BuiltInCategoryUniIdentities::BICU_STRUCTURE_BEAM && 
                element->GetBasicInformation()->GetType()->GetElementParameters()->GetParameterByUid(
                    PARAMETER_UID(CategorySubTypeBuiltInParameter))->GetValueAsInt() == 
                    (int)StructureBeamType::VariableSection))
            {
                return false;
            }

            const gcmp::IPositionCurve2d* posCurve2d = dynamic_cast<const gcmp::IPositionCurve2d*>(posBehavior->GetPositionGeometry());
            return posCurve2d != nullptr;
        }

        return false;
    }

    bool GbmpBreakPickFilterForLocalSnap::AllowGraphicsNode(const gcmp::IGraphicsNodeReference& nodeReference) const
    {
        auto gnode = nodeReference.GetUntransformedIntersectGraphicsNode();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(gnode != nullptr, L"gnode指针无效",L"GDMPLab",L"2024-03-30");
        if (gnode->GetVisibility() == gcmp::GraphicsNodeVisibility::Always)
            return true;
        else
            return false;
    }

    //double GetCurvePointRatrio(const gcmp::ICurve2d* pCurve2d, const gcmp::Vector2d& pickPoint)
    //{
    //    DBG_WARN_AND_RETURN_UNLESS(pCurve2d != nullptr, -1.0, L"pCurve2d为空", L"GDMPLab", L"2023-12-20");
    //    double length = pCurve2d->GetLength();
    //    Vector2d startPoint = pCurve2d->GetStartPoint();
    //    GeometryType type = pCurve2d->GetType();
    //    double distance = 1.0;
    //    if (type == GeometryType::Line2d)
    //        distance = Vector2dUtils::Distance(startPoint, pickPoint);
    //    else if (type == GeometryType::Arc2d)
    //    {
    //        const IArc2d* pArc2d = quick_cast<IArc2d>(pCurve2d);
    //        distance = 2 * pArc2d->GetRadius() * asin(Vector2dUtils::Distance(startPoint, pickPoint) / (2 * pArc2d->GetRadius()));
    //    }
    //    return distance / length;
    //}

    int IsCollinearInPolyCurveSegment(const gcmp::IPolyCurve * pPolyCurve, const ICurve2d * pCurve2d, bool& isCompleteCoincidence)
    {
        DBG_WARN_AND_RETURN_UNLESS(pPolyCurve != nullptr, -1, L"pPolyCurve为空",L"GDMPLab",L"2024-03-30");
        isCompleteCoincidence = false;
        int curveCount = pPolyCurve->GetCurveCount();
        for (int index = 0; index < curveCount; ++index)
        {
            OwnerPtr<ICurve2d> opCurve2d = pPolyCurve->GetCurve(index);
            if (!opCurve2d)
                continue;
            CurvesPositionTypes curvesPosType = AlgorithmPositionJudge::GetPosition(opCurve2d.get(), pCurve2d);
            if (curvesPosType == CurvesPositionTypes::CompleteCoincidence)
            {
                isCompleteCoincidence = true;
                return index;
            }

            if (curvesPosType == CurvesPositionTypes::Curve1OnCurve2 || curvesPosType == CurvesPositionTypes::Curve2OnCurve1
                || curvesPosType == CurvesPositionTypes::PartialCoincidence)
            {
                return index;
            }
        }
        return -1;
    }

    void CalculateNewPolyCurveSegmentLineWidth(IDocument* pDoc, const IModelView* pModelView, const IDrawingPolyCurve* pOldDrawingPolyCurve, 
        OwnerPtr<IPolyCurve> opNewPolyCurve)
    {
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMPLab",L"2024-03-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(pModelView, L"pModelView为空",L"GDMPLab",L"2024-03-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(pOldDrawingPolyCurve, L"pDrawingPolyCurve为空",L"GDMPLab",L"2024-03-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(opNewPolyCurve, L"opNewPolyCurve为空",L"GDMPLab",L"2024-03-30");
        double lineWidth = pOldDrawingPolyCurve->GetLineWidth();
        bool isLineTypeGen = pOldDrawingPolyCurve->IsEnableLinetypeGeneration();
        ElementId styleId = pOldDrawingPolyCurve->GetGraphicsStyleId();

        const IPolyCurve* pOriginPolyCurve = pOldDrawingPolyCurve->GetGeometryCurve();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pOriginPolyCurve, L"pOriginPolyCurve为空",L"GDMPLab",L"2024-03-30");
        std::vector<std::pair<double, double>> newSegmentWidths;
        if (MathUtils::IsLessThan(lineWidth, 0.0))
        {
            bool isCompleteCoincidence = true;
            // 1. 计算每一段的线宽值
            for (int index = 0; index < opNewPolyCurve->GetCurveCount(); ++index)
            {
                OwnerPtr<ICurve2d> opCurve2d = opNewPolyCurve->GetCurve(index);
                if (!opCurve2d)
                    continue;

                int originIndex = IsCollinearInPolyCurveSegment(pOriginPolyCurve, opCurve2d.get(), isCompleteCoincidence);
                if (originIndex < 0 || originIndex > pOriginPolyCurve->GetCurveCount())
                    continue;

                std::pair<double, double> segmentLineWidth = pOldDrawingPolyCurve->GetSegmentLineWidth(originIndex);
                if (!isCompleteCoincidence)
                {
                    OwnerPtr<ICurve2d> opOriginCurve2d = pOriginPolyCurve->GetCurve(originIndex);
                    double para = opCurve2d->GetLength() / opOriginCurve2d->GetLength();
                    if (MathUtils::IsGreaterThan(para, 1.0))
                    {
                        isCompleteCoincidence = true;
                        continue;
                    }
                    double newLineWidth = 1.0;
                    if (opOriginCurve2d->GetStartPoint() == opCurve2d->GetStartPoint())
                    {
                        newLineWidth = min(segmentLineWidth.first, segmentLineWidth.second) + para * abs(segmentLineWidth.first - segmentLineWidth.second);
                        newSegmentWidths.emplace_back(std::make_pair(segmentLineWidth.first, newLineWidth));
                    }
                    else
                    {
                        newLineWidth = min(segmentLineWidth.first, segmentLineWidth.second) + (1 - para) * abs(segmentLineWidth.first - segmentLineWidth.second);
                        newSegmentWidths.emplace_back(std::make_pair(newLineWidth, segmentLineWidth.second));
                    }
                    isCompleteCoincidence = true;
                }
                else
                    newSegmentWidths.emplace_back(std::make_pair(segmentLineWidth.first, segmentLineWidth.second));
            }
        }

        IDrawingPolyCurve* pNewPolyCurve = IDrawingPolyCurve::Create(pDoc, pModelView, TransferOwnership(opNewPolyCurve));
        if (pNewPolyCurve)
        {
            pNewPolyCurve->SetIsEnableLinetypeGeneration(isLineTypeGen);
            pNewPolyCurve->SetGraphicsStyleId(styleId);

            if (newSegmentWidths.empty())
                pNewPolyCurve->SetLineWidth(lineWidth);
            else
                FOR_EACH_INDEX(widthIndex, newSegmentWidths)
            {
                auto newWidthPair = newSegmentWidths[widthIndex];
                pNewPolyCurve->SetSegmentLineWidth(widthIndex, newWidthPair.first, newWidthPair.second);
            }
        }
    }
}

GbmpActionBreak::GbmpActionBreak()
    : m_isUserCancelled(false), m_snap(nullptr), m_opInputDialog(nullptr)
{
}

GbmpActionBreak::~GbmpActionBreak()
{
    CleanupTempGraphicsShape();
}

void GbmpActionBreak::InitInputDialog()
{
    std::vector<OwnerPtr<IControlDefinition>> dlgInputCtrlDefines;
    OwnerPtr<IRadioButtonGroupDefinition> opDelTypeGroupDef = IRadioButtonGroupDefinition::Create(ACTION_BREAK_POLYCURVE, GBMP_TR(L"选项"));
    opDelTypeGroupDef->AddRadioButton(s_titleOneBreak, s_titleOneBreak, s_titleOneBreak);
    opDelTypeGroupDef->AddRadioButton(s_titleTwoBreak, s_titleTwoBreak, s_titleTwoBreak);
    opDelTypeGroupDef->SetCurrentRadioButton(ms_twoPtBreak ? s_titleTwoBreak : s_titleOneBreak);
    dlgInputCtrlDefines.emplace_back(TransferOwnership(opDelTypeGroupDef));
    dlgInputCtrlDefines.emplace_back(TransferOwnership(opDelTypeGroupDef));
    m_opInputDialog = IActionInputDialog::Create(GBMP_TR(L"打断"), TransferOwnership(dlgInputCtrlDefines), this);
    DBG_WARN_AND_RETURN_VOID_UNLESS(m_opInputDialog, L"m_opInputDialog为空",L"GDMPLab",L"2024-03-30");
    if (m_opInputDialog && m_opInputDialog->GetInputEvent())
    {
        m_opInputDialog->GetInputEvent()->Add(this);
    }
}

void GbmpActionBreak::InitAction(IUiView* pCurrentView)
{
    InitInputDialog();
    gcmp::IDocument* pDbDoc = GetDoc();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDbDoc != nullptr, L"pDbDoc指针为空",L"GDMPLab",L"2024-03-30");
    ISelection::Get()->Clear(pDbDoc);
    IHighlights::Get()->Clear();
    m_breakStatus = BreakStatus::Not_Started;
    m_oFilterForLocalSnap = NEW_AS_OWNER_PTR(GbmpBreakPickFilterForLocalSnap, pDbDoc);

    EnterPickBreakFirstPointState();
}

void gcmp::GbmpActionBreak::On(gcmp::IActionInputEventArgs * pArgs)
{
    auto msg = pArgs->GetInputMessage();
    if (msg.first == ACTION_BREAK_POLYCURVE)
    {
        ms_twoPtBreak = (msg.second.cast<std::wstring>() == s_titleOneBreak ? false : true);
        CancelChildAction();
        CleanupTempGraphicsShape();
        EnterPickBreakFirstPointState();
    }
}

void GbmpActionBreak::OnChildActionFinished(IUiView* pCurrentView, const ActionOutput& childActionReturnParam)
{
    if (m_isUserCancelled)
    {
        CleanupTempGraphicsShape();
        MarkFinishStatus(ActionFinishStatus::Successful);
        return;
    }

    if (m_breakStatus == BreakStatus::Selecting_FirstPt)
    {
        if (m_snap == nullptr)
        {
            EnterPickBreakFirstPointState();
            return;
        }

        const GraphicsNodeReferenceOwnerPtrSet& nodeReferences = m_snap->GetSnappedGraphicsNodeReferences();
        DBG_WARN_AND_RETURN_VOID_UNLESS(nodeReferences.size() > 0, L"选择集为空",L"GDMPLab",L"2024-03-30");
        auto pElem = (*nodeReferences.begin()).get();
        m_breakElemId = pElem->GetElementId();
        if (m_breakElemId == ElementId::InvalidID)
        {
            NotificationUtils::PostNotification(NotificationTypes::WarningNotificationType, GBMP_TR(L"请选择有效的图形进行打断!"));
            EnterPickBreakFirstPointState();
            return;
        }

        // 两点打断进入选择第二打断点
        if (ms_twoPtBreak)
            EnterPickBreakSecondPointState();
        else
        {
            BreakElement();
            IHighlights::Get()->Clear();
            UpdateView();
            EnterPickBreakFirstPointState();
        }

    }
    else if (m_breakStatus == BreakStatus::Selecting_SecondPt)
    {
        if (m_snap == nullptr)
        {
            EnterPickBreakSecondPointState();
            return;
        }

        const GraphicsNodeReferenceOwnerPtrSet& nodeReferences = m_snap->GetSnappedGraphicsNodeReferences();
        DBG_WARN_AND_RETURN_VOID_UNLESS(nodeReferences.size() > 0, L"选择集为空",L"GDMPLab",L"2024-03-30");
        auto pElem = (*nodeReferences.begin()).get();
        ElementId secondSelElemId = pElem->GetElementId();
        if (secondSelElemId != m_breakElemId || m_breakElemId == ElementId::InvalidID)
        {
            NotificationUtils::PostNotification(NotificationTypes::WarningNotificationType, GBMP_TR(L"请选择同一个图形上的点进行两点打断!"));
            EnterPickBreakSecondPointState();
            return;
        }

        IHighlights::Get()->Clear();
        BreakElement();
        UpdateView();
        EnterPickBreakFirstPointState();
    }
    //UpdateView();
}

void GbmpActionBreak::EnterPickBreakFirstPointState()
{
    m_isUserCancelled = false;

    SetPromptMessage(GBMP_TR(L"选择打断点位置"));
    SetCursorType(CursorType::CrossHairCursor);
    m_breakStatus = BreakStatus::Selecting_FirstPt;

    PickPointExchangeData exchangeDate(&m_breakPt, &m_isUserCancelled, &m_breakPts, &m_snap);
    gcmp::OwnerPtr<IPickPointAction> upPickPointAction = IPickPointAction::Create(exchangeDate);
    DBG_WARN_AND_RETURN_VOID_UNLESS(upPickPointAction, L"upPickPointAction为空",L"GDMPLab",L"2024-03-30");
    // TODO：这里设置ISnapSettings不会在IPickPointAction::DoSnap中使用（DoSnap中使用全局捕捉设置），后续是否会调整？
    const ISnapContext* pContext = upPickPointAction->GetSnapContext();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pContext, L"pContext为空",L"GDMPLab",L"2024-03-30");
    //OwnerPtr<ISnapSettings> snapSettings = pContext->GetSnapSettings();
    //snapSettings->DisableAllSnaps();
    //snapSettings->SetCanSnapMiddlePoint(true);
    //snapSettings->SetCanSnapNearest(true);
    //snapSettings->SetCanSnapQuadrants(true);

    OwnerPtr<ISnapContext> opContext = pContext->Clone();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opContext, L"pContext->Clone()失败",L"GDMPLab",L"2024-03-30");
    //opContext->SetSnapSettings(snapSettings.get());
    opContext->SetFilterForLocalSnap(m_oFilterForLocalSnap.get());
    upPickPointAction->SetSnapContext(opContext.get());
    upPickPointAction->SetCursorType(GetCursorType());

    StartChildAction(TransferOwnership(upPickPointAction));
}

void GbmpActionBreak::EnterPickBreakSecondPointState()
{
    m_isUserCancelled = false;

    SetPromptMessage(GBMP_TR(L"选择第二打断点位置"));
    SetCursorType(CursorType::CrossHairCursor);
    m_breakStatus = BreakStatus::Selecting_SecondPt;

    PickPointExchangeData exchangeDate(&m_breakPt2, &m_isUserCancelled, &m_breakPts, &m_snap);
    gcmp::OwnerPtr<IPickPointAction> upPickPointAction = IPickPointAction::Create(exchangeDate);
    DBG_WARN_AND_RETURN_VOID_UNLESS(upPickPointAction, L"upPickPointAction为空",L"GDMPLab",L"2024-03-30");
    // TODO：这里设置ISnapSettings不会在IPickPointAction::DoSnap中使用（DoSnap中使用全局捕捉设置），后续是否会调整？
    const ISnapContext* pContext = upPickPointAction->GetSnapContext();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pContext, L"pContext为空",L"GDMPLab",L"2024-03-30");
    //OwnerPtr<ISnapSettings> snapSettings = pContext->GetSnapSettings();
    //snapSettings->DisableAllSnaps();
    //snapSettings->SetCanSnapMiddlePoint(true);
    //snapSettings->SetCanSnapNearest(true);
    //snapSettings->SetCanSnapQuadrants(true);

    OwnerPtr<ISnapContext> opContext = pContext->Clone();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opContext, L"pContext->Clone()失败",L"GDMPLab",L"2024-03-30");
    //opContext->SetSnapSettings(snapSettings.get());
    opContext->SetFilterForLocalSnap(m_oFilterForLocalSnap.get());
    upPickPointAction->SetSnapContext(opContext.get());
    upPickPointAction->SetCursorType(GetCursorType());

    StartChildAction(TransferOwnership(upPickPointAction));
}

void GbmpActionBreak::BreakElement() const
{
    IDocument* pDoc = GetDoc();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc != nullptr, L"pDoc为空",L"GDMPLab",L"2024-03-30");
    if (m_snap == nullptr)
    {
        return;
    }

    // m_breakPt 是工作平面的点，把它投影到世界坐标系里元素的定位线上
    // ptOnBaseLine 就是通过 m_breakPt 捕捉点计算得到的构件上的点（即构件实际断开的位置）
    IModelView* pModelView = IUiDocumentViewManager::Get()->GetCurrentUiView()->GetModelView();
    DBG_WARN_AND_RETURN_VOID_UNLESS(pModelView, L"获得当前ModelView失败",L"GDMPLab",L"2024-03-30");
    OwnerPtr<gcmp::IPlane> opPlane = pModelView->GetWorkPlane();
    DBG_WARN_AND_RETURN_VOID_UNLESS(opPlane, L"opPlane为空",L"GDMPLab",L"2024-03-30");
    Vector3d normal = opPlane->GetNormal();
    DBG_WARN_AND_RETURN_VOID_UNLESS(normal.IsValid(), L"获得当前工作平面的法向量失败",L"GDMPLab",L"2024-03-30");
    Intervald interVald(gcmp::Constants::MATH_NEGINFINITY,gcmp::Constants::MATH_INFINITY);
    OwnerPtr<ILine3d> porLine = ILine3d::Create(m_breakPt,normal,interVald);
    DBG_WARN_AND_RETURN_VOID_UNLESS(porLine, L"创建直线失败！",L"GDMPLab",L"2024-03-30");

    const IElement* pBreakElement = GetDoc()->GetElement(m_breakElemId);
    DBG_WARN_AND_RETURN_VOID_UNLESS(pBreakElement, L"pBreakElement为空",L"GDMPLab",L"2024-03-30");
    if (pBreakElement->GetBasicInformation()->GetCategoryUid() == BuiltInCategoryUniIdentities::BICU_DRAWING_POLYCURVE)
    {
        const IGenericElement* pGenericElement = quick_cast<IGenericElement>(pBreakElement);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pGenericElement, L"pGenericElement为空",L"GDMPLab",L"2024-03-30");
        const IDrawingPolyCurve* pDrawingPolyCurve = quick_cast<IDrawingPolyCurve>(pGenericElement->GetExternalObject());
        DBG_WARN_AND_RETURN_VOID_UNLESS(pDrawingPolyCurve, L"pDrawingPolyCurve为空",L"GDMPLab",L"2024-03-30");
        const IPolyCurve* pPolyCurve = pDrawingPolyCurve->GetGeometryCurve();
        Coordinate3d coord = pDrawingPolyCurve->GetCoordinate();
        Vector3d pickedPrjPt(m_breakPt);
        Vector2d para;
        if (!AlgorithmProject::Project(m_breakPt, normal, opPlane.get(), para, pickedPrjPt))
            return ;

        std::vector<gcmp::OwnerPtr<gcmp::IPolyCurve>> opCurvesResult;
        std::wstring errorMsg;
        bool isSuccess = false;
        if (!ms_twoPtBreak)
            isSuccess = BreakUtils::BreakPolyCurve(pPolyCurve, pickedPrjPt, coord, opCurvesResult);
        else
        {
            Vector3d pickedPrjPt2(m_breakPt2);
            Vector2d para;
            if (!AlgorithmProject::Project(m_breakPt2, opPlane->GetNormal(), opPlane.get(), para, pickedPrjPt2))
                return;
            isSuccess = BreakUtils::BreakPolyCurveBy2Pt(pPolyCurve, pickedPrjPt, pickedPrjPt2, coord, opCurvesResult);
        }
        if (!isSuccess)
        {
            NotificationUtils::PostNotification(NotificationTypes::WarningNotificationType, L"打断不满足约束条件");
            return ;
        }

        OwnerPtr<IUserTransaction> ut = IUserTransaction::Create(pDoc, GBMP_TR(L"多段线打断"));
        DBG_WARN_AND_RETURN_VOID_UNLESS(ut != nullptr, L"事务创建不成功",L"GDMPLab",L"2024-03-30");
        if (opCurvesResult.size() == 1)
        {
            if (!ms_twoPtBreak)
                return;

            OwnerPtr<ICurve2d> opOriginFirstCurve2d = pPolyCurve->GetCurve(0);
            DBG_WARN_AND_RETURN_VOID_UNLESS(opOriginFirstCurve2d, L"opOriginFirstCurve2d为空",L"GDMPLab",L"2024-03-30");
            Vector2d polyCurveStartPt = opOriginFirstCurve2d->GetStartPoint();
            Vector2d breakLocal1 = coord.GetLocalPointXY(m_breakPt);
            Vector2d breakLocal2 = coord.GetLocalPointXY(m_breakPt2);
            if (polyCurveStartPt == breakLocal1 && breakLocal1 == breakLocal2)
                return;
            CalculateNewPolyCurveSegmentLineWidth(pDoc, pModelView, pDrawingPolyCurve, TransferOwnership(opCurvesResult[0]));
        }
        else
        {
            if (!opCurvesResult.empty() && opCurvesResult[0] && opCurvesResult[1])
            {
                CalculateNewPolyCurveSegmentLineWidth(pDoc, pModelView, pDrawingPolyCurve, TransferOwnership(opCurvesResult[0]));
                CalculateNewPolyCurveSegmentLineWidth(pDoc, pModelView, pDrawingPolyCurve, TransferOwnership(opCurvesResult[1]));
            }
        }

        if (m_breakElemId.IsValid())
            pDoc->DeleteElement(m_breakElemId);

        bool result = ut->Commit();
        DBG_WARN_AND_RETURN_VOID_UNLESS(result, L"ut为空",L"GDMPLab",L"2024-03-30");
    }
    else
    {
        if (ms_twoPtBreak)
        {
            // m_snap的空指针判定在函数最前面
            const GraphicsNodeReferenceOwnerPtrSet& nodeReferences = m_snap->GetSnappedGraphicsNodeReferences();
            DBG_WARN_AND_RETURN_VOID_UNLESS(nodeReferences.size()>0, L"选择集为空",L"GDMPLab",L"2024-03-30");
            auto& opNodeRef = *nodeReferences.begin();
            DBG_WARN_AND_RETURN_VOID_UNLESS(opNodeRef, L"opNodeRef为空",L"GDMPLab",L"2024-03-30");
            const ICurve3d* pCurve3d = opNodeRef->GetUntransformedIntersectCurve3d();

            if (!pCurve3d)
                return;
            std::vector<OwnerPtr<ICurve3d>> opCurvesResult;
            bool isSuccess = BreakUtils::BreakCurve3dBy2Pt(pCurve3d, m_breakPt, m_breakPt2, opPlane.get(), opCurvesResult);
            if (!isSuccess)
            {
                NotificationUtils::PostNotification(NotificationTypes::WarningNotificationType, L"打断不满足约束条件");
                return;
            }

            OwnerPtr<IUserTransaction> ut = IUserTransaction::Create(GetDoc(), GBMP_TR(L"两点打断"));
            DBG_WARN_AND_RETURN_VOID_UNLESS(ut != nullptr, L"事务创建不成功",L"GDMPLab",L"2024-03-30");
            // 外部用户可以定制，目前以模型线为例
            FOR_EACH(opCurve3d, opCurvesResult)
            {
                if (!opCurve3d)
                    continue;
                IModelLine::Create(pDoc, TransferOwnership(opCurve3d));
            }

            if (m_breakElemId.IsValid())
                pDoc->DeleteElement(m_breakElemId);

            bool result = ut->Commit();
            DBG_WARN_AND_RETURN_VOID_UNLESS(result, L"ut为空",L"GDMPLab",L"2024-03-30");
            return;
        }

        Vector3d ptOnBaseLine;
        Vector3d ptOnSecLine;
        std::vector<Coordinate3d> css;
        const gcmp::IElementPosition *posBehavior = pBreakElement->GetElementPosition();
        if (posBehavior != nullptr)
        {
            if (dynamic_cast<const IPositionCurve2d*>(posBehavior->GetPositionGeometry()) != nullptr)
            {
                css = posBehavior->GetWorldCoordinateSystems();
            }
            else
            {
                DBG_WARN(L"pBreakElement不适用于打断",L"GDMPLab",L"2024-03-30");
            }
        }

        OwnerPtr<ILine3d> pLine3d;
        OwnerPtr<IArc3d> pGArc3d;
        if (css.size() == 2)
        {
            const Vector3d& startPnt = css[0].Origin();
            const Vector3d& endPnt = css[1].Origin();
            pLine3d = ILine3d::Create(startPnt, endPnt);
            double minDistance = 0.0;
            bool success = AlgorithmMinimalDistance::Calculate(porLine.get(), pLine3d.get(), ptOnSecLine, ptOnBaseLine, minDistance);
        }
        else if (css.size() == 3)
        {
            const Vector3d& startPnt = css[0].Origin();
            const Vector3d& endPnt = css[1].Origin();
            const Vector3d& onArcPnt = css[2].Origin();
            pGArc3d = IArc3d::Create(startPnt, endPnt, onArcPnt);
            double minDistance = 0.0;
            bool success = AlgorithmMinimalDistance::Calculate(porLine.get(), pGArc3d.get(), ptOnSecLine, ptOnBaseLine, minDistance);
        }
        else
        {
            DBG_WARN(L"请添加相应曲线转换",L"GDMPLab",L"2024-03-30");
        }

        OwnerPtr<IUserTransaction> ut = IUserTransaction::Create(GetDoc(), GBMP_TR(L"打断"));
        DBG_WARN_AND_RETURN_VOID_UNLESS(ut != nullptr, L"事务创建不成功",L"GDMPLab",L"2024-03-30");

        {
            //实例化具体打断行为，需要产品自己提供。例子如下：
            OwnerPtr<IElementBreakBehavior> opElementBreakBehavior = nullptr;
            //         if (xxx)
            //         {
            //             opElementBreakBehavior = NEW_AS_OWNER_PTR(GradientWallBreakBehavior_Deprecated);
            //         }
            //         else if (xxx)
            //         {
            //             opElementBreakBehavior = NEW_AS_OWNER_PTR(BeamBreakBehavior_Deprecated);
            //         }
            if (pBreakElement->GetBasicInformation()->GetCategoryUid() == BuiltInCategoryUniIdentities::BICU_STRUCTURE_WALL &&
                pBreakElement->GetBasicInformation()->GetType()->GetElementParameters()->GetParameterByUid(
                    PARAMETER_UID(CategorySubTypeBuiltInParameter))->GetValueAsInt() ==
                    (int)StructureWallType::VariableSection
                )
            {
                opElementBreakBehavior = NEW_AS_OWNER_PTR(StructureGradientWallBreakBehavior);
            }
            else if (pBreakElement->GetBasicInformation()->GetCategoryUid() == BuiltInCategoryUniIdentities::BICU_STRUCTURE_BEAM)
            {
                opElementBreakBehavior = NEW_AS_OWNER_PTR(StructureBeamBreakBehavior);
            }

            BreakElementResult result = BreakUtils::BreakElement(pDoc, m_breakElemId, ptOnBaseLine, opElementBreakBehavior.get());

            if (!result.NewId.IsValid())
            {
                NotificationUtils::PostNotification(NotificationTypes::WarningNotificationType, result.BreakStatus);
            }
        }

        bool result = ut->Commit();
        DBG_WARN_AND_RETURN_VOID_UNLESS(result, L"ut为空",L"GDMPLab",L"2024-03-30");
    }

}
